﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Data;
using Microsoft.Kinect;
using HCIProject;
using System.Xml.Linq;
using Microsoft.Speech.Recognition;
using Microsoft.Speech.AudioFormat;

namespace HCIproject
{
    /// <summary>
    /// Interaction logic for StaticDucks.xaml
    /// </summary>

    public partial class StaticDucks : Window
    {


        #region DucksManager
        static string ImagesAdd = "pack://application:,,,/Images/";


        //constant variables
        int widthPic = 0;
        int heightPic = 0;
        int pickType = 0;
        public const int windowWidth = 640;
        public const int windowHeight = 480;
        int x = 0;
        int y = 0;
        int z = 0;
        int CountDuck = 0;
        DataTable Ducktbl = new DataTable("Duck");
        DataRow dr = null;

        public StaticDucks()
        {
            Ducktbl.Columns.Add("x");
            Ducktbl.Columns.Add("y");
            Ducktbl.Columns.Add("z");
            Ducktbl.Columns.Add("id");
            Ducktbl.Columns.Add("size");
            InitializeComponent();
        }
        private void AddnewDuck()
        {

            if (CountDuck >= Convert.ToInt32(txtDucksCount.Text))
            {
                MessageBox.Show("you can not add more ducks");
                return;
            }

            #region withMouse
            if (x == 0 && y == 0)
            {
                dr = Ducktbl.NewRow();
                Point MousePosition = Mouse.GetPosition(Application.Current.MainWindow);
                Image img = new Image();
                img.Width = widthPic;
                img.Height = heightPic;
                var uriSource = new Uri(ImagesAdd + "duckBig.png");
                img.Source = new BitmapImage(uriSource);
                mainCanvas.Children.Add(img);

                if (MousePosition.Y > windowHeight - heightPic)
                {
                    Canvas.SetTop(img, MousePosition.Y - heightPic);
                    dr["y"] = MousePosition.Y - heightPic;
                }
                else if (MousePosition.Y > heightPic)
                {
                    Canvas.SetTop(img, MousePosition.Y - heightPic / 2);
                    dr["y"] = MousePosition.Y - heightPic / 2;
                }
                else
                {
                    Canvas.SetTop(img, MousePosition.Y);
                    dr["y"] = MousePosition.Y;
                }
                if (MousePosition.X > windowWidth - widthPic)
                {
                    Canvas.SetLeft(img, MousePosition.X - widthPic);
                    dr["x"] = MousePosition.X - widthPic;
                }
                else if (MousePosition.X > widthPic)
                {
                    Canvas.SetLeft(img, MousePosition.X - widthPic / 2);
                    dr["x"] = MousePosition.X - widthPic / 2;
                }
                else
                {
                    Canvas.SetLeft(img, MousePosition.X);
                    dr["x"] = MousePosition.X;
                }
                dr["z"] = "0";
                dr["id"] = "img" + CountDuck;
                if (SmallRadioBtn.IsChecked == true)
                    dr["size"] = Convert.ToInt32(ImageTypes.Small);
                else if (BigRadioBtn.IsChecked == true)
                    dr["size"] = Convert.ToInt32(ImageTypes.Big);
                else
                    dr["size"] = Convert.ToInt32(ImageTypes.Medium);

                Ducktbl.Rows.Add(dr);
                CountDuck++;
            }

            else
            {
            #endregion withMouse
                dr = Ducktbl.NewRow();
                Image img = new Image();

                img.Width = widthPic;
                img.Height = heightPic;
                var uriSource = new Uri(ImagesAdd + "duckBig.png");
                img.Source = new BitmapImage(uriSource);
                mainCanvas.Children.Add(img);

                if (y > windowHeight - heightPic)
                {
                    Canvas.SetTop(img, y - heightPic);
                    dr["y"] = y - heightPic;
                }
                else if (y > heightPic)
                {
                    Canvas.SetTop(img, y - heightPic / 2);
                    dr["y"] = y - heightPic / 2;
                }
                else
                {
                    Canvas.SetTop(img, y);
                    dr["y"] = y;
                }
                if (x > windowWidth - widthPic)
                {
                    Canvas.SetLeft(img, x - widthPic);
                    dr["x"] = x - widthPic;
                }
                else if (x > widthPic)
                {
                    Canvas.SetLeft(img, x - widthPic / 2);
                    dr["x"] = x - widthPic / 2;
                }
                else
                {
                    Canvas.SetLeft(img, x);
                    dr["x"] = x;
                }
                dr["z"] = z;
                dr["id"] = "img" + CountDuck;



                if (SmallRadioBtn.IsChecked == true)
                    dr["size"] = Convert.ToInt32(ImageTypes.Small);
                else if (BigRadioBtn.IsChecked == true)
                    dr["size"] = Convert.ToInt32(ImageTypes.Big);
                else
                    dr["size"] = Convert.ToInt32(ImageTypes.Medium);
                Ducktbl.Rows.Add(dr);
                CountDuck++;

            }
        }
        private void btnSave_click(object sender, RoutedEventArgs e)
        {
            string imageAddress = ""; //FIXME take the screen shut and save the address
            DirectoryInfo DI = new DirectoryInfo(Environment.CurrentDirectory);
            string xmlfile = DI.Parent.Parent.FullName.ToString() + "\\File\\" + comboLevel.Text + ".xml";
            //Played Joint Values
            string joint = "";
            if (ChbLF.IsChecked == true)
                joint += "LF,";
            if (ChbRF.IsChecked == true)
                joint += "RF,";
            if (ChbLH.IsChecked == true)
                joint += "LH,";
            if (ChbRH.IsChecked == true)
                joint += "RH,";
            if (joint.Length == 0)
                joint = "LF,RF,";
            joint = joint.Remove(joint.Length - 1, 1);

            //write into the xml file
            XDocument doc = new XDocument(
                    new XDeclaration("1.0", "utf-8", "yes"),
                    new XComment("Configuration of the game in " + comboLevel.Text + " Mode"),
                    new XElement("DuckTable", new XElement("LevelName", comboLevel.Text),
                                              new XElement("PlayJoint", joint),
                                              new XElement("NumberOfDuck", txtDucksCount.Text),
                                              new XElement("AppearanceTime", txtAppearance.Text),
                                              new XElement("PlayTime", txtPlayTime.Text),
                                              new XElement("Age", comboAge.Text),
                                              new XElement("ImageAddress", imageAddress))
                                );
            for (int i = 0; i < Ducktbl.Rows.Count; i++)
            {
                doc.Element("DuckTable").Add(new XElement("Duck",
                                                            new XAttribute("id", Ducktbl.Rows[i]["id"]),
                                                            new XElement("x", Ducktbl.Rows[i]["x"]),
                                                            new XElement("y", Ducktbl.Rows[i]["y"]),
                                                            new XElement("z", Ducktbl.Rows[i]["z"]),
                                                            new XElement("size", Ducktbl.Rows[i]["size"]))
                                             );
            }
            doc.Save(xmlfile);
            MessageBox.Show("The duck positions are set now you can play the game.");
        }
        private void mainGrid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            AddnewDuck();
        }
        private void mainGrid_MouseMove(object sender, MouseEventArgs e)
        {
            Point p = Mouse.GetPosition(Application.Current.MainWindow);
            labelxyReal.Content = "X= " + p.X + ", Y= " + p.Y;
        }

        private void BigRadioBtn_Checked(object sender, RoutedEventArgs e)
        {
            if (BigRadioBtn.IsChecked == true)
            {
                widthPic = 100;
                heightPic = 100;
                pickType = Convert.ToInt32(ImageTypes.Big);
            }
        }
        private void MediumRadioBtn_Checked(object sender, RoutedEventArgs e)
        {
            if (MediumRadioBtn.IsChecked == true)
            {
                widthPic = 50;
                heightPic = 50;
                pickType = Convert.ToInt32(ImageTypes.Medium);
            }
        }
        private void SmallRadioBtn_Checked(object sender, RoutedEventArgs e)
        {
            if (SmallRadioBtn.IsChecked == true)
            {
                widthPic = 30;
                heightPic = 30;
                pickType = Convert.ToInt32(ImageTypes.Small);
            }
        }

        private void editMark_SelectedIndexChanged(object sender, SelectionChangedEventArgs e)
        {
            int editJointIndex = comboLevel_Copy.SelectedIndex;
            switch (editJointIndex)
            {
                case 0:
                    editJoint = JointType.HandLeft;
                    break;
                case 1:
                    editJoint = JointType.HandRight;
                    break;
                case 2:
                    editJoint = JointType.FootLeft;
                    break;
                case 3:
                    editJoint = JointType.FootRight;
                    break;
                default:
                    editJoint = JointType.FootLeft;
                    break;
            }
        }
        #endregion DucksManager

        #region Kinect
        KinectSensor kinectSensor;

        readonly ColorStreamManager colorManager = new ColorStreamManager();
        readonly DepthStreamManager depthManager = new DepthStreamManager();
        SkeletonDisplayManager skeletonDisplayManager;
        Skeleton[] skeletons;
        readonly ContextTracker contextTracker = new ContextTracker();

        JointType editJoint = JointType.FootLeft;

        private SpeechRecognitionEngine speechEngine;

        void Kinects_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                case KinectStatus.Connected:
                    if (kinectSensor == null)
                    {
                        kinectSensor = e.Sensor;
                        Initialize();
                    }
                    break;
                case KinectStatus.Disconnected:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        MessageBox.Show("Kinect was disconnected");
                    }
                    break;
                case KinectStatus.NotReady:
                    break;
                case KinectStatus.NotPowered:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        MessageBox.Show("Kinect is no more powered");
                    }
                    break;
                default:
                    MessageBox.Show("Unhandled Status: " + e.Status);
                    break;
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                //listen to any status change for Kinects
                KinectSensor.KinectSensors.StatusChanged += Kinects_StatusChanged;

                //loop through all the Kinects attached to this PC, and start the first that is connected without an error.
                foreach (KinectSensor kinect in KinectSensor.KinectSensors)
                {
                    if (kinect.Status == KinectStatus.Connected)
                    {
                        kinectSensor = kinect;
                        break;
                    }
                }

                if (KinectSensor.KinectSensors.Count == 0)
                    MessageBox.Show("No Kinect found");
                else
                    Initialize();
                //Define the background for the Edit mode
                ImageBrush myBrush = new ImageBrush();
                Image image = new Image();
                image.Source = new BitmapImage(
                    new Uri(
                       "pack://application:,,,/Images/myback.jpg"));
                myBrush.ImageSource = image.Source;
                setting.Background = myBrush;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Initialize()
        {
            if (kinectSensor == null)
                return;

            kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
            kinectSensor.ColorFrameReady += kinectRuntime_ColorFrameReady;

            kinectSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
            kinectSensor.DepthFrameReady += kinectSensor_DepthFrameReady;

            kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters
            {
                Smoothing = 0.5f,
                Correction = 0.5f,
                Prediction = 0.5f,
                JitterRadius = 0.05f,
                MaxDeviationRadius = 0.04f
            });
            kinectSensor.SkeletonFrameReady += kinectRuntime_SkeletonFrameReady;

            kinectSensor.Start();

            RecognizerInfo ri = GetKinectRecognizer();

            if (null != ri)
            {
                this.speechEngine = new SpeechRecognitionEngine(ri.Id);

                var setCmd = new Choices();

                //add recognizable voice command (the ones in lowercase)
                setCmd.Add(new SemanticResultValue("here", "SET"));
                setCmd.Add(new SemanticResultValue("set", "SET"));
                setCmd.Add(new SemanticResultValue("put", "SET"));

                var gb = new GrammarBuilder { Culture = ri.Culture };
                gb.Append(setCmd);

                var g = new Grammar(gb);

                speechEngine.LoadGrammar(g);

                speechEngine.SpeechRecognized += SpeechRecognized;

                speechEngine.SetInputToAudioStream(
                    kinectSensor.AudioSource.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
                speechEngine.RecognizeAsync(RecognizeMode.Multiple);
            }
            else
            {
                //do nothing
            }

            skeletonDisplayManager = new SkeletonDisplayManager(kinectSensor, kinectCanvas);
            kinectDisplay.DataContext = colorManager;   // if we want to display Color Stream
            //kinectDisplay.DataContext = depthManager;   // if we want to display Depth Stream
        }

        private void kinectRuntime_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            using (var frame = e.OpenColorImageFrame())
            {
                if (frame == null)
                    return;

                colorManager.Update(frame);
            }
        }

        void kinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            using (var frame = e.OpenDepthImageFrame())
            {
                if (frame == null)
                    return;

                depthManager.Update(frame);
            }
        }

        void kinectRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            using (SkeletonFrame frame = e.OpenSkeletonFrame())
            {
                if (frame == null)
                    return;

                frame.GetSkeletons(ref skeletons);

                if (skeletons.All(s => s.TrackingState == SkeletonTrackingState.NotTracked))
                    return;

                ProcessFrame(frame);
            }
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
            {
                string value;
                recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return recognizer;
                }
            }

            return null;
        }

        void ProcessFrame(SkeletonFrame frame)
        {
            Dictionary<int, string> stabilities = new Dictionary<int, string>();
            foreach (var skeleton in frame.GetSkeletons())
            {
                if (skeleton.TrackingState != SkeletonTrackingState.Tracked)
                    continue;

                contextTracker.Add(skeleton.Position.ToVector3(), skeleton.TrackingId);
                stabilities.Add(skeleton.TrackingId, contextTracker.IsStableRelativeToCurrentSpeed(skeleton.TrackingId) ? "Stable" : "Non stable");
                if (!contextTracker.IsStableRelativeToCurrentSpeed(skeleton.TrackingId))
                    continue;

                foreach (Joint joint in skeleton.Joints)
                {
                    if (joint.TrackingState != JointTrackingState.Tracked)
                        continue;
                    if ((joint.JointType == editJoint))
                    {
                        try
                        {
                            // update [x,y,z] of the editJoint for tracking purpose
                            var colorPoint = kinectSensor.CoordinateMapper.MapSkeletonPointToColorPoint(joint.Position, kinectSensor.ColorStream.Format);
                            x = Convert.ToInt32(colorPoint.X);
                            y = Convert.ToInt32(colorPoint.Y);
                            var depthPoint = kinectSensor.CoordinateMapper.MapSkeletonPointToDepthPoint(joint.Position, kinectSensor.DepthStream.Format);
                            z = Convert.ToInt32(depthPoint.Depth);

                            labelxy.Content = "X= " + x + ", Y= " + y + ", Z = " + z;

                        }
                        catch (Exception)
                        {
                            MessageBox.Show("");
                        }
                    }
                }
            }

            skeletonDisplayManager.Draw(frame.GetSkeletons(), false);    // true = seatedMode
            //stabilitiesList.ItemsSource = stabilities;
        }

        private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            // Speech utterance confidence below which we treat speech as if it hadn't been heard
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                switch (e.Result.Semantics.Value.ToString())
                {
                    case "SET":
                        //TODO: add codes here to handle voice recognition event
                        //this.Close();
                        AddnewDuck();
                        break;
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            try
            {
                Clean();
                MainWindow mw = new MainWindow();
                mw.Show();
                //this.Close();
            }
            catch
            {
                // do nothing
            }

        }

        private void Clean()
        {
            if (kinectSensor != null)
            {
                kinectSensor.DepthFrameReady -= kinectSensor_DepthFrameReady;
                kinectSensor.SkeletonFrameReady -= kinectRuntime_SkeletonFrameReady;
                kinectSensor.ColorFrameReady -= kinectRuntime_ColorFrameReady;
                kinectSensor.AudioSource.Stop();
                kinectSensor.Stop();
                //kinectSensor = null;
            }
            if (null != this.speechEngine)
            {
                this.speechEngine.SpeechRecognized -= SpeechRecognized;
                //this.speechEngine.SpeechRecognitionRejected -= SpeechRejected;
                this.speechEngine.RecognizeAsyncStop();
            }
        }

        #endregion Kinect

        private void btnMainMenu_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void btnReset_Click(object sender, RoutedEventArgs e)
        {
            MediumRadioBtn.IsChecked = ChbRF.IsChecked = ChbLF.IsChecked = true;
            ChbLH.IsChecked = ChbRH.IsChecked = false;
            txtDucksCount.Text = "5";
            comboLevel_Copy.SelectedIndex = 2;
            comboLevel.SelectedIndex = 0;
            comboAge.SelectedIndex = 2;
            txtPlayTime.Text = "1";
            txtAppearance.Text = "";
            Ducktbl.Clear();
            dr = null;
            CountDuck = 0;
            mainCanvas.Children.Clear();


        }


    }
}


